#pragma once

#include "Business/Business.h"
#include "Business/DealingHistory.h"
#include "Business/CustomerPosition.h"
#include "Business/Leg.h"
#include "StatusMachine/StatusMachine.h"
#include "Business/Dealing.h"
#include "Business/Band.h"
#include "Business/Policy/ClassicBusinessPosition.h"
#include "Business/ArbitrageLegs.h"
#include "Business/ContractMatcher.h"

class Customer;
class Funds;
class MarketStorage;
class QueueContext;
class DealingRepository;
class CancellationRepository;

class ArbitrageOffsetBusiness;

namespace ArbitrageOffsetBusinessStatusMachine {
	class Event
	{
	public:
		enum TYPE {NONE,MARKET_EVENT,DEALING_EVENT,START_COMMAND, EDIT_COMMAND,STOP_COMMAND};
		explicit Event(TYPE type = NONE);
		explicit Event(Market const& market);
		explicit Event(Dealing const& dealing);
		int GetType() const { return m_type; }
		Market const& GetMarket() const { return *(Market*)m_data; }
		Dealing const& GetDealing() const { return *(Dealing*)m_data; }

	private:
		int m_type;
		char m_data[1024];
	};

	typedef Status<ArbitrageOffsetBusiness,Event> StatusT;
	class StatusBase : public StatusT
	{
	public:
		StatusBase(std::string const& name, ArbitrageOffsetBusiness& context):StatusT(name,context){}
	private:
		virtual void DoBefore();
	};
	typedef ConditionBase<ArbitrageOffsetBusiness,Event> ConditionT;
	typedef StatusMachine<ArbitrageOffsetBusiness,Event> StatusMachineT;


#define DEFINE_CONDITION(condition_name)						\
	class condition_name : public ConditionT					\
	{															\
	public:														\
	explicit condition_name(ArbitrageOffsetBusiness& context)	\
	:ConditionT(context){}										\
	virtual ~condition_name() {}								\
	private:													\
	virtual bool DoCheck(Event const& event);					\
	}

	DEFINE_CONDITION(OffsetMarketCondition);
	DEFINE_CONDITION(MinorOffsetOrderedCondition);
	DEFINE_CONDITION(MinorOffsettedCondition);
	DEFINE_CONDITION(MinorOffsetAllCancelledCondition);
	DEFINE_CONDITION(PartialOffsetedCondition);
	DEFINE_CONDITION(AllOffsetedCondition);
	DEFINE_CONDITION(StartCondition);
	DEFINE_CONDITION(EditCondition);
#undef DEFINE_CONDITION

#define DEFINE_STATUS(class_name,name)					\
	class class_name : public StatusBase					\
	{														\
	public:													\
	explicit class_name(ArbitrageOffsetBusiness& context)		\
	:StatusBase(name,context){}							\
	virtual ~class_name(){}								\
	private:											\
	virtual void DoExecute(Event const& event);			\
	}

	DEFINE_STATUS(StartStatus,"start");

	DEFINE_STATUS(WaitToClearStatus,"wait to clear");
	DEFINE_STATUS(MinorOffsetSentStatus,"minor offset sent");
	DEFINE_STATUS(MinorOffsetCancelSentStatus,"minor offset ordered");
	DEFINE_STATUS(MajorOffsetSentStatus,"major offset sent");

	DEFINE_STATUS(EndStatus,"end");
	DEFINE_STATUS(EditableStatus,"editable");
#undef DEFINE_STATUS
}

class Dealing;
class Cancellation;
class TimeoutNotifier;


class ArbitrageOffsetBusiness : public AutoUpdateUIBusiness<ArbitrageOffsetBusiness>
{
public:
	class Argument
	{
	public:
		Argument(ArbitrageContracts const& contracts, bool isOffsetToday,int quantity, Band const& band)
			:m_contracts(contracts),m_isOffsetToday(isOffsetToday),m_quantity(quantity),m_band(band)
		{
		}
		ArbitrageContracts const& GetContracts() const { return m_contracts; }
		Operation::TYPE GetOffsetOperation() const { return m_isOffsetToday ? Operation::OFFSET_TODAY : Operation::OFFSET; }
		int GetQuantity() const { return m_quantity; }
		Band const& GetBand() const { return m_band; }

		void UpdateBand(Argument const& other)
		{
			m_band = other.GetBand();
		}
	private:
		ArbitrageContracts m_contracts;
		bool m_isOffsetToday;
		int m_quantity;
		Band m_band;
	};

	class Snapshot
	{
	public:
		Snapshot(char const* status,int minorDirection,
			int majorQuantity,int minorQuantity,double balance,double profit)
			:m_status(status),m_minorDirection(minorDirection),
			m_majorQuantity(majorQuantity),m_minorQuantity(minorQuantity),
			m_balance(balance),m_profit(profit)
		{
		}
		char const* GetStatus() const {return m_status;}
		int GetMinorDirection() const {return m_minorDirection;}
		int GetMinorQuantity() const {return m_minorQuantity;}
		int GetMajorQuantity() const {return m_majorQuantity;}
		double GetBalance() const {return m_balance;}
		double GetProfit() const {return m_profit;}

		bool operator==(Snapshot const& other) const
		{
			return m_status == other.m_status && m_minorDirection == other.m_minorDirection &&
				m_minorQuantity == other.m_minorQuantity  && m_balance == other.m_balance && m_profit == other.m_profit;
		}
		bool operator!=(Snapshot const& other) const
		{
			return !operator==(other);
		}
	private:
		char const * m_status;
		int m_minorDirection;
		int m_majorQuantity;
		int m_minorQuantity;
		double m_balance;
		double m_profit;
	};
public:

	ArbitrageOffsetBusiness(Customer& customer,
		Argument const& argument,
		MarketStorage const& marketStorage,
		QueueContext& queueContext, 
		DealingRepository& dealingRepository,
		CancellationRepository& cancellationRepository);

	virtual ~ArbitrageOffsetBusiness();

	Argument const& GetArgument() const { return m_argument; }
	int GetMajor() const { return m_argument.GetContracts().GetMajor(); }
	int GetMinor() const { return m_argument.GetContracts().GetMinor(); }
	char const* GetMajorCode() const;
	char const* GetMinorCode() const;
	char const* GetFartherCode() const;
	char const* GetNearbyCode() const;
	ClassicBusinessPosition const& GetPositions() const { return m_positions; }
	bool IsEditable() const;

	Snapshot GetSnapshot();
	void UpdateUI(Snapshot const& snapshot);

	void Init();

	void OffsetMinor();
	void CancelMinorOffset();
	void OffsetMajorByLimitPrice();

	bool CanOffset() const;
	bool HasSamePosition() const;
	bool IsZero() const;
private:

	virtual void DoOnInit();
	virtual void DoOnMarket(Market const& market);
	virtual void DoOnPendingMarket(PendingMarket const& pendingMarket);;
	virtual void DoOnDealing(Dealing const& dealing);

	virtual void DoOnTimeout(RequestTimeout const& requestTimeout);
	virtual void DoOnTrade(Trade const& trade);
	virtual void DoOnCommand(Command const& command);
	virtual void DoOnExchangeStatus(int exchangeStatus);

	void UpdateArgument(Argument const& argument);
	Order CreateOrder(Leg const& leg,double price, int quantity);
	Dealing& PushOrder(Order const& order);
	Cancellation& PushCancellation(Cancellation const& cancellation);

	Argument m_argument;
	MarketStorage const& m_marketStorage;
	QueueContext& m_queueContext;
	DealingRepository& m_dealingRepository;
	CancellationRepository& m_cancellationRepository;
	ArbitrageOffsetBusinessStatusMachine::StatusMachineT* m_machine;
	ArbitrageLegs m_legs;
	ClassicBusinessPosition m_positions;

	DealingHistory m_minorOffsetDealingHistory;
};
